package org.anachronos.jeep.ui.trace;

import java.util.ArrayList;
import java.util.List;

import org.anachronos.jeep.model.trace.Invocation;
import org.anachronos.jeep.ui.trace.TraceFilterParser.TraceFilterSwitch;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

public class TraceFilter extends ViewerFilter {
	private static final String[] EMPTY_FILTER_LABELS = new String[] {
		"<Name>", "<Name>", "<Numeric>", "<Numeric>", "<Numeric>", "<Numeric>", "<Numeric>"	
	};
	private static final TraceFilterParser[] TRACE_FILTER_PARSER = new TraceFilterParser[] {
		TraceFilterParser.NAME, TraceFilterParser.NAME, 
		TraceFilterParser.NUMERIC, TraceFilterParser.NUMERIC, TraceFilterParser.NUMERIC,
		TraceFilterParser.NUMERIC, TraceFilterParser.NUMERIC
	};
	private final TraceFilterSwitch[] filterSwitches = 
		new TraceFilterSwitch[EMPTY_FILTER_LABELS.length]; 
	private final String[] columnFilterStrings = new String[] {
			"", "", "", "", "", "", ""	
	};

	public String getLabel(final int columnIndex) {
		return isFilterSet(columnIndex) ? 
				getColumnFilter(columnIndex) : EMPTY_FILTER_LABELS[columnIndex];
	}

	public boolean isFilterSet(final int columnIndex) {
		final boolean isFilterSet = filterSwitches[columnIndex] != null 
		&& !"".equals(columnFilterStrings[columnIndex]);
		return isFilterSet;
	}

	public String getColumnFilter(final int columnIndex) {
		return columnFilterStrings[columnIndex];
	}

	public void setColumnFilter(final int columnIndex, final String filter) {
		columnFilterStrings[columnIndex] = filter;
		filterSwitches[columnIndex] = TRACE_FILTER_PARSER[columnIndex].parse(filter, columnIndex);
	}

	private boolean anyFilterSet() {
		for (final TraceFilterSwitch filterSwitch : filterSwitches) {
			if (filterSwitch != null) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Object[] filter(Viewer viewer, Object parent, Object[] elements) {
		if (viewer instanceof TreeViewer && anyFilterSet()) {
			final List<Object> filteredElements = new ArrayList<Object>();
			for (final Object element : elements) {
				if (element instanceof EObject) {
					if (element instanceof Invocation) {
						final Invocation invocation = (Invocation) element;
						if (filter(invocation)) {
							filteredElements.add(invocation);
						}
					}
				} else {
					filteredElements.add(element);
				}
			}
			return filteredElements.toArray();
		} else {
			return super.filter(viewer, parent, elements);
		}
	}

	private boolean filter(final Invocation invocation) {
		final boolean filterMethodInvocations = 
			filterChildren((EList<? extends Invocation>) invocation.getMethodInvocation());
		final boolean filterSqlStatementExecutions = 
			filterChildren((EList<? extends Invocation>) invocation.getSqlStatementExecution());
		final boolean filterTransactions = 
			filterChildren((EList<? extends Invocation>) invocation.getTransaction());
		final boolean filterElement = filterMethodInvocations | 
			filterSqlStatementExecutions | 
			filterTransactions | 
			select(null, null, invocation);
		return filterElement;
	}

	private boolean filterChildren(final EList<? extends Invocation> invocations) {
		for (final Invocation child : invocations) {
			if (filter(child)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean select(Viewer viewer, Object parentElement, Object element) {
		if (element instanceof EObject) {
			final EObject elem = (EObject) element;
			for (int columnIndex = 0; columnIndex < columnFilterStrings.length; columnIndex++) {
				if (isFilterSet(columnIndex)) {
					if (!filterSwitches[columnIndex].doSwitch(elem)) {
						return false;
					}
				}
			}
		} 
		return true;
	}
}
